把技術選得對,之後每一次需求變動都像換鞋帶,而不是換腳。以下是我在
eap-ai-client採用的技術棧與背後取捨,聚焦「可維護、可治理」。
在講完我的mcp工具實作後,此篇文章要來介紹我用來串接LLM模型的服務,這個服務初衷是希望我能透過自然語言來對我的eap工具進行操作,像是我可以輸入"請幫我依據現在市場最低賣價,買入20單位的電力"那LLM就會幫我們呼叫mcp工具來進行操作,那也可以使用自然語言來進行整體市場環境的模擬。
action、toolName、params、planVersion),@NotNull、@Pattern)做 Schema 檢核,拒收不合格回覆。@ConfigurationProperties 設定 llm.provider/baseUrl/model/apiKey,由 Profile/配置決定注入哪個 Adapter(OpenAI、Ollama…)。切換供應商=改設定+換 Adapter Bean,不動業務程式。McpToolClient 這個 Port;工具清單或傳輸協定(HTTP → gRPC)變動由底層 Adapter 吸收。ai-service 不管工具實作細節,維持和 LLM 一樣的解耦關係。
ai-service只負責「理解與下決策」→「呼叫工具」的編排;供應商與工具實作細節都被 Adapter 吃掉,日後要換 LLM 或擴工具,不會牽動核心服務。
McpToolClient 作為 Port,底層以 WebClient/Feign Adapter 實作;getOrderBook, getMarketMetrics)直接放行。placeOrder, cancelOrder, registerUser)走確認機制:
AiChatService 檢視 action → 3) 未帶「確認旗標」則回要求確認的自然語言 → 4) 收到確認後才呼叫。@ConfigurationProperties:llm.*、mcp.*、resilience.* 一次綁定,避免硬編碼。dev/staging/prod 切換 endpoint、重試策略、模型名稱。UserInputError(使用者/模型參數不完整)ToolRejectedError(未經確認或政策禁止)DownstreamError(MCP 或下游)SystemError(不可預期)extra 擴充位,避免破壞性變更。toolName 直接拒絕。使用者/系統 → AiChatController → AiChatService
→ ChatClient(LLM)
↘ Plan(JSON) 驗證/正規化
→(Read-only? 直接)
→(State-changing? 要確認)
→ McpToolClient → eap-mcp(工具)→ 下游服務
把上面理念落進程式:application.yml 與 @ConfigurationProperties 的切分、ChatClient/MCP 的 Bean 設計、Resilience4j 與 OTel 的最小實用配置,一鍵換 Provider、不改核心碼。